#pragma once

namespace RoomFW {

//
// Class:	ILoggerUtil
//
// Description:	Log macro utility class.
//
// Author:	Andrew Sage
//
struct ILoggerUtil
{
	//
	// Method:	FileName
	//
	// Description:	Return pointer to start of filename in full path.  Only handles '\'.
	//
	// Parameters:
	//
	//	path - Full path.
	//
	// Return:	Pointer to start of filename.  May be path if path is just a file name.
	//
	static const char* FileName(const char* path)
	{
		const char* retVal = path;
		const char* p = path;
		while (*p)
		{
			if ('\\' == *p)
			{
				retVal = (p + 1);
			}
			++p;
		}
		return retVal;
	}
};

//
// Method:	LOG_X
//
// Description:	Log macros.
//
// Parameters:
//
//	logger - ILogger*
//
//	verbosity - LoggingVerbosity
//
//	format - printf format string
//
//	argx - printf argument.
//
// Return:	None.
//
#define LOG_0(logger, verbosity, format) \
	(logger)->log((verbosity), RoomFW::ILoggerUtil::FileName(__FILE__), __LINE__, (format));

#define LOG_1(logger, verbosity, format, arg1) \
	(logger)->logV((verbosity), RoomFW::ILoggerUtil::FileName(__FILE__), __LINE__, (format), (arg1));

#define LOG_2(logger, verbosity, format, arg1, arg2) \
	(logger)->logV((verbosity), RoomFW::ILoggerUtil::FileName(__FILE__), __LINE__, (format), (arg1), (arg2));

#define LOG_3(logger, verbosity, format, arg1, arg2, arg3) \
	(logger)->logV((verbosity), RoomFW::ILoggerUtil::FileName(__FILE__), __LINE__, (format), (arg1), (arg2), (arg3));

#define LOG_4(logger, verbosity, format, arg1, arg2, arg3, arg4) \
	(logger)->logV((verbosity), RoomFW::ILoggerUtil::FileName(__FILE__), __LINE__, (format), (arg1), (arg2), (arg3), (arg4));

#define LOG_5(logger, verbosity, format, arg1, arg2, arg3, arg4, arg5) \
	(logger)->logV((verbosity), RoomFW::ILoggerUtil::FileName(__FILE__), __LINE__, (format), (arg1), (arg2), (arg3), (arg4), (arg5));

#define LOG_6(logger, verbosity, format, arg1, arg2, arg3, arg4, arg5, arg6) \
	(logger)->logV((verbosity), RoomFW::ILoggerUtil::FileName(__FILE__), __LINE__, (format), (arg1), (arg2), (arg3), (arg4), (arg5), (arg6));

#define LOG_7(logger, verbosity, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7) \
	(logger)->logV((verbosity), RoomFW::ILoggerUtil::FileName(__FILE__), __LINE__, (format), (arg1), (arg2), (arg3), (arg4), (arg5), (arg6), (arg7));

#define LOG_8(logger, verbosity, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) \
	(logger)->logV((verbosity), RoomFW::ILoggerUtil::FileName(__FILE__), __LINE__, (format), (arg1), (arg2), (arg3), (arg4), (arg5), (arg6), (arg7), (arg8));

#define LOG_9(logger, verbosity, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) \
	(logger)->logV((verbosity), RoomFW::ILoggerUtil::FileName(__FILE__), __LINE__, (format), (arg1), (arg2), (arg3), (arg4), (arg5), (arg6), (arg7), (arg8), (arg9));

//
// Logging verbosity
//
enum LoggingVerbosity
{
	LV_SILENT	= 0x00,	// No logging.
	LV_ERROR	= 0x01,	// Errors
	LV_WARNING	= 0x02, // Warnings
	LV_SYSTEM	= 0x04,	// System level events.
	LV_SOFTWARE	= 0x08,	// Software level events.
	LV_MESSAGE	= 0x10,	// Endpoint (port) messages.
	LV_TRACE	= 0x20,	// Detailed software tracing.
	LV_SPEED	= 0x40, // Lower verbosity System speed test logging.
	LV_SPEED2	= 0x80, // Higher verbosity development speed test logging.
	LV_ALL		= 0xFF,	// Log everything.
};

//
// Class:	ILogger
//
// Description:	Astract logger interface.
//
// Author:	Andrew Sage
//
struct ILogger
{
	//
	// Method:	~ILogger
	//
	// Description:	Virtual destructor to ensure derived class destructors called.
	//
	virtual ~ILogger() {};

	//
	// Method:	setConsole
	//
	// Description:	Enable/Disable console output and set buffer size.
	//
	// Parameters:
	//
	//	enable -	Enable flag.
	//
	//	maxLines -	Number of lines in console buffer.
	//
	// Return:	None.
	//
	virtual void setConsole(bool enable, DWORD maxLines = 0) = 0;

	//
	// Method:	setFile
	//
	// Description:	Enable/Disable file output and set file name.
	//
	// Parameters:
	//
	//	enable -	Enable flag.
	//
	//	fileName -	Name of log file.
	//
	//	cached -	True to cache logging in memory.  Writes file on close.
	//
	// Return:	None.
	//
	virtual void setFile(bool enable, const char* fileName = 0, size_t maxSize = 0, size_t maxFiles = 0, bool cached = false) = 0;

	//
	// Method:	setDebugOutput
	//
	// Description:	Enable/Disable debug output.
	//
	// Parameters:
	//
	//	enable -	Enable flag.
	//
	// Return:	None.
	//
	virtual void setDebugOutput(bool enable) = 0;

	//
	// Method:	setVerbosity
	//
	// Description:	Set verbosity(s) to log.  Single LoggingVerbosity value or
	//				multiple LoggingVerbosity values OR'd together.
	//
	// Parameters:
	//
	//	verbosity -	Verbosity(s) to log.
	//
	// Return:	None.
	//
	virtual void setVerbosity(uint32_t verbosity) = 0;

	//
	// Method:	log
	//
	// Description:	Log something.  Use the macros, rather than calling directly.
	//
	// Parameters:
	//
	//	timestamp -	Current time.
	//
	//	verbosity -	Verbosity.
	//
	//	file -	Source file called from.
	//
	//	line -	Source file line called from.
	//
	//	text -	Actual log message.
	//
	// Return:	None.
	//
	virtual void log(LoggingVerbosity verbosity,
					 const char* file,
					 const int line,
					 const char* text) = 0;
	virtual void logV(LoggingVerbosity verbosity,
					 const char* file,
					 const int line,
					 const char* format,
					 ...) = 0;

	//
	// Method:	isLv[Verbosity]
	//
	// Description:	Test if a given verbosity is enabled.
	//
	// Parameters:
	//
	//	verbosity - Verbosity to check for.
	//
	// Return:	True if logging that verbosity.
	//
	virtual bool isLvError() const = 0;
	virtual bool isLvWarning() const = 0;
	virtual bool isLvSystem() const = 0;
	virtual bool isLvSoftware() const = 0;
	virtual bool isLvMessage() const = 0;
	virtual bool isLvTrace() const = 0;
	virtual bool isLvSpeed() const = 0;
	virtual bool isLvSpeed2() const = 0;

	// Return the current log file name, if logging to a file, otherwise return an empty std::string.
	virtual std::string getFileName() const = 0;
};

//
// Class:	MethodTracer
//
// Description:	Constructor/Destructor class to trace method entry/exit
//
// Author:	Andrew Sage
//
struct MethodTracer
{
	//
	// Method:	MethodTracer
	//
	// Description:	Constructor.  Logs method entry.
	//
	// Parameters:
	//
	//	logger -	Logger pointer.
	//
	//	method -	Method name.  Use fully qualified class::method.
	//
	MethodTracer(ILogger* logger, const char* method) :
		logger_(logger),
		method_(method)
	{
		LOG_1(logger_, LV_TRACE, "{ %s", method_.c_str());
	}

	//
	// Method:	~MethodTracer
	//
	// Description:	Destructor.  Logs method exit.
	//
	~MethodTracer(void)
	{
		LOG_1(logger_, LV_TRACE, "} %s", method_.c_str());
	}

	//
	// Save logger and method name.
	//
	ILogger* logger_;
	std::string method_;
};
#define TRACE_METHOD(logger, method) MethodTracer thisMethodTracer((logger), (method));

}
